{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Causal additive models with unobserved variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "from search.FCMBased.lingam.CAMUV import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data generation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_noise(n):\n",
    "    noise = ((np.random.rand(1, n)-0.5)*5).reshape(n)\n",
    "    mean = get_random_constant(0.0,2.0)\n",
    "    noise += mean\n",
    "    return noise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def causal_func(cause):\n",
    "    a = get_random_constant(-5.0,5.0)\n",
    "    b = get_random_constant(-1.0,1.0)\n",
    "    c = int(random.uniform(2,3))\n",
    "    return ((cause+a)**(c))+b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_random_constant(s,b):\n",
    "    constant = random.uniform(-1.0, 1.0)\n",
    "    if constant>0:\n",
    "        constant = random.uniform(s, b)\n",
    "    else:\n",
    "        constant = random.uniform(-b, -s)\n",
    "    return constant"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_data(n):\n",
    "    causal_pairs = [[0,1],[0,3],[2,4]]\n",
    "    intermediate_pairs = [[2,5]]\n",
    "    confounder_pairs = [[3,4]]\n",
    "\n",
    "    n_variables = 6\n",
    "\n",
    "    data = np.zeros((n, n_variables)) # observed data\n",
    "    confounders = np.zeros((n, len(confounder_pairs))) # data of unobserced common causes\n",
    "\n",
    "    # Adding external effects\n",
    "    for i in range(n_variables):\n",
    "        data[:,i] = get_noise(n)\n",
    "    for i in range(len(confounder_pairs)):\n",
    "        confounders[:,i] = get_noise(n)\n",
    "        confounders[:,i] = confounders[:,i] / np.std(confounders[:,i])\n",
    "\n",
    "    # Adding the effects of unobserved common causes\n",
    "    for i, cpair in enumerate(confounder_pairs):\n",
    "        cpair = list(cpair)\n",
    "        cpair.sort()\n",
    "        data[:,cpair[0]] += causal_func(confounders[:,i])\n",
    "        data[:,cpair[1]] += causal_func(confounders[:,i])\n",
    "\n",
    "    for i1 in range(n_variables)[0:n_variables]:\n",
    "        data[:,i1] = data[:,i1] / np.std(data[:,i1])\n",
    "        for i2 in range(n_variables)[i1+1:n_variables+1]:\n",
    "            # Adding direct effects between observed variables\n",
    "            if [i1, i2] in causal_pairs:\n",
    "                data[:,i2] += causal_func(data[:,i1])\n",
    "            # Adding undirected effects between observed variables mediated through unobserved variables\n",
    "            if [i1, i2] in intermediate_pairs:\n",
    "                interm = causal_func(data[:,i1])+get_noise(n)\n",
    "                interm = interm / np.std(interm)\n",
    "                data[:,i2] += causal_func(interm)\n",
    "    \n",
    "    return data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Please note that the sample size set below is $n=2000$. Please reduce the sample size if the execution time is too long."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "sample_size = 2000\n",
    "data = create_data(sample_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"datageneration.png\" width=\"1000\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Execute the program\n",
    "### Usage\n",
    "CAMUV.execute(X, alpha, num_explanatory_vals) is the method to infer causal graphs.\n",
    "\n",
    "- Arguments\n",
    "    - X: matrix\n",
    "    - alpha: the alpha level for independence testing\n",
    "    - num_explanatory_vals: the maximum number of variables to infer causal relationships. This is equivalent to d in the paper.\n",
    "\n",
    "- Returns\n",
    "    - P: P[i] contains the indices of the parents of Xi\n",
    "    - U: The indices of variable pairs having UCPs or UBPs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "P,U = CAMUV.execute(data,0.01,3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Direct causal relationships"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "child: 1,  parents: {0}\n",
      "child: 3,  parents: {0}\n",
      "child: 4,  parents: {2}\n"
     ]
    }
   ],
   "source": [
    "for i, result in enumerate(P):\n",
    "    if not len(result) == 0:\n",
    "        print(\"child: \" + str(i) + \",  parents: \"+ str(result))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Variable pairs having a UCP or a UBP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{2, 5}\n",
      "{3, 4}\n"
     ]
    }
   ],
   "source": [
    "for result in U:\n",
    "    print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.6"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "source": [],
    "metadata": {
     "collapsed": false
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}